<?php

/*
+---------------------------------------------------------------------------
|
|   iShudan.class.php (php 4.x)
|
|   by Benjam Welker
|   http://www.iohelix.net
|   based on works by
|   http://www.sourceforge.com
|
+---------------------------------------------------------------------------
|
|   > PHP Go module
|   > Date started: 2007-01-13
|   >  Last edited: 2007-01-13
|
|   > Module Version Number: 0.8
|
|   $Id$
+---------------------------------------------------------------------------
*/

class iShudan
{
	var $board; // the board array
	var $oldboard; // the old board array, for ko testing purposes
	var $empty_board; // the empty board for display purposes
	var $boardsize; // the size of the board
	var $handicap; // the black player's handicap
	var $color; // the color of the current move
	var $turn; // (true / false) player's turn ?
	var $history; // the history array
	var $move; // the current move
	var $moves; // the previous moves array
	var $move_count; // the number of moves performed
	var $moved_stone_x; // the moved stone's x position
	var $moved_stone_y; // the moved stone's y position
	var $black_prisoners; // the black prisoners
	var $white_prisoners; // the white prisoners
	var $passes; // the number of consecutive passes
	var $resign; // whether or not the player resigned
	var $dummy; // ???
	var $visited; // the list of visited points
	var $stones_visited; // the list of visited stones
	var $freedoms_visited; // the list of visited freedoms
	var $removed; // the list of removed stones
	var $owner; // the owner of a group with freedoms
	var $freedom; // the number of freedoms in a group
	var $error; // any errors that may be encountered

	function iShudan( )
	{
		// constructor
	}


	function set_board($board)
	{
		$this->board = $board;
	}


	function get_board( )
	{
		return $this->board;
	}


	function set_boardsize($boardsize)
	{
		// make sure the boarsize is valid
		if ((19 < $boardsize) || (9 > $boardsize) || (1 == ($boardsize % 2)))
		{
			$this->error = 'Incorrect boardsize given, boardsize must be an odd number between 9 and 19, inclusive.';
			return false;
		}

		$this->boardsize = $boardsize;

		return true;
	}


	function set_handicap($handicap)
	{
		// make sure the handicap is valid
		if ((9 < $handicap) || (2 > $handicap))
		{
			$this->error = 'Incorrect handicap given, handicap must be between 2 and 9, inclusive.';
			return false;
		}

		$this->handicap = $handicap;

		return true;
	}


	function set_moves($moves)
	{
		$this->moves = $moves;
		$this->move_count = count($this->moves);
	}

	// plays the moves array to the board, used to recreate board from moves array
	function play_moves($until = null)
	{
		$this->black_prisoners = 0;
		$this->white_prisoners = 0;
		$this->passes = 0;
		$this->resign = false;
		$this->history = array( );

		$until = is_null($until) ? $this->move_count + 1 : $until;

		if ($this->handicap > 0)
		{
			$this->board = $this->set_handicap_stones( );
		}

		for ($i = 0; $i < $this->move_count; ++$i)
		{
			for ($j = 0; $j < strlen($this->moves[$i]); $j = $j + 3)
			{
				list($this->color, $y, $x) = stringtocord(substr($this->moves[$i], $j, 3));

				if ('e' == $this->color)
				{
					if ($until > $i)
					{
						$this->insert_empty($y, $x);
					}

					++$this->white_prisoners;
				}
				else if ('r' == $this->color)
				{
					if ($until > $i)
					{
						$this->insert_empty($y, $x);
					}

					++$this->black_prisoners;
				}
				else if ((23 == $y) && (23 == $x))
				{
					if (($i == ($this->move_count - 1)) && ($until == ($this->move_count + 1)) || ($i == ($until - 1)))
					{
						$this->moved_stone_x = 0;
						$this->moved_stone_y = 0;
					}

					if ('b' == $this->color)
					{
						$cprint = 'Black';
					}

					if ('w' == $this->color)
					{
						$cprint = 'White';
					}

					++$this->passes;
					$this->history[$i] = 'P';
				}
				else if ((25 == $y) && (25 == $x))
				{
					if (($i == ($this->move_count - 1)) && ($until == ($this->move_count + 1)) || ($i == ($until - 1)))
					{
						$this->moved_stone_x = 0;
						$this->moved_stone_y = 0;
					}

					if ('b' == $this->color)
					{
						$cprint = 'Black';
						$this->resign = 1;
					}

					if ('w' == $this->color)
					{
						$cprint = 'White';
						$this->resign = 2;
					}

					$this->passes      = 0;
					$this->history[$i] = 'R';
				}
				else
				{
					if ((($i == ($this->move_count - 1)) && ($until == ($this->move_count + 1))) || ($i < $until))
					{
						$this->moved_stone_x = $x;
						$this->moved_stone_y = $y;
					}

					if ($until > $i)
					{
						$this->board[$y][$x] = $this->color;
					}

					if ('b' == $this->color)
					{
						$cprint = 'Black';
					}

					if ('w' == $this->color)
					{
						$cprint = 'White';
					}

					$this->history[$i] = strtolower(xtoalt($x)) . ($this->boardsize - $y);
					$this->passes = 0;
				}
			}

			if ($i == ($this->move_count - 2))
			{
				$this->oldboard = $this->board;
			}
		}

#		return array(
#			$board ,
#			$oldboard ,
#			$passes ,
#			$resign ,
#			$bprisoners ,
#			$wprisoners ,
#			$mstonex ,
#			$mstoney ,
#			$history ,
#			$countm ,
#		);
	}


	function set_handicap_stones( )
	{
		if (2 <= $this->handicap)
		{
			if ($this->boardsize >= 13)
			{
				$this->board[3][$this->boardsize - 4] = 'b';
			}

			if ($size_of_the_board < 13)
			{
				$this->board[2][$this->boardsize - 3] = 'b';
			}

			if ($this->boardsize >= 13)
			{
				$this->board[$this->boardsize - 4][3] = 'b';
			}

			if ($this->boardsize < 13)
			{
				$this->board[$this->boardsize - 3][2] = 'b';
			}
		}

		if (3 <= $this->handicap)
		{
			if ($this->boardsize >= 13)
			{
				$this->board[$this->boardsize - 4][$this->boardsize - 4] = 'b';
			}

			if ($this->boardsize < 13)
			{
				$this->board[$this->boardsize - 3][$this->boardsize - 3] = 'b';
			}
		}

		if (4 <= $this->handicap)
		{
			if ($this->boardsize >= 13)
			{
				$this->board[3][3] = 'b';
			}

			if ($this->boardsize < 13)
			{
				$this->board[2][2] = 'b';
			}
		}

		if (5 <= $this->handicap)
		{
			if ($this->boardsize >= 13)
			{
				$this->board[ceil($this->boardsize / 2) - 1][$this->boardsize - 4] = 'b';
			}

			if ($this->boardsize < 13 && $this->boardsize > 7)
			{
				$this->board[ceil($this->boardsize / 2) - 1][$this->boardsize - 3] = 'b';
			}
		}

		if (6 <= $this->handicap)
		{
			if ($this->boardsize >= 13)
			{
				$this->board[ceil($this->boardsize / 2) - 1][3] = 'b';
			}

			if ($this->boardsize < 13 && $this->boardsize > 7)
			{
				$this->board[ceil($this->boardsize / 2) - 1][2] = 'b';
			}
		}

		if (7 <= $this->handicap)
		{
			if ($this->boardsize >= 13)
			{
				$this->board[3][ceil($this->boardsize / 2) - 1] = 'b';
			}

			if ($this->boardsize < 13 && $this->boardsize > 7)
			{
				$this->board[2][ceil($this->boardsize / 2) - 1] = 'b';
			}
		}

		if (8 <= $this->handicap)
		{
			if ($this->boardsize >= 13)
			{
				$this->board[$this->boardsize - 4][ceil($this->boardsize / 2) - 1] = 'b';
			}

			if ($this->boardsize < 13 && $this->boardsize > 7)
			{
				$this->board[$this->boardsize - 3][ceil($this->boardsize / 2) - 1] = 'b';
			}
		}

		if (9 == $this->handicap)
		{
			if ($this->boardsize >= 13)
			{
				$this->board[ceil($this->boardsize / 2) - 1][ceil($this->boardsize / 2) - 1] = 'b';
			}

			if ($this->boardsize < 13 && $this->boardsize > 7)
			{
				$this->board[ceil($this->boardsize / 2) - 1][ceil($this->boardsize / 2) - 1] = 'b';
			}
		}
	}


	// insert an empty field on the given position into the board array
	function insert_empty($y, $x)
	{
		if ($y == 0)
		{
			if ($x == 0)
			{
				$stone = 'lt';
			}
			else if ($x == $this->boardsize - 1)
			{
				$stone = 'rt';
			}
			else
			{
				$stone = 'mt';
			}
		}
		else if ($y == $this->boardsize - 1)
		{
			if ($x == 0)
			{
				$stone = 'lb';
			}
			else if ($x == $this->boardsize - 1)
			{
				$stone = 'rb';
			}
			else
			{
				$stone = 'mb';
			}
		}
		else
		{
			if ($x == 0)
			{
				$stone = 'lm';
			}
			else if ($x == $this->boardsize - 1)
			{
				$stone = 'rm';
			}
			else
			{
				if ($this->boardsize >= 13 && ($y == 3 && ($x == 3 || $x == ceil($this->boardsize / 2) - 1 || $x == $this->boardsize - 4)))
				{
					$stone = 'ko';
				}
				else if ($this->boardsize >= 13 && ($y == ceil($this->boardsize / 2) - 1 && ($x == 3 || $x == ceil($this->boardsize / 2) - 1 || $x == $this->boardsize - 4)))
				{
					$stone = 'ko';
				}
				else if ($this->boardsize >= 13 && ($y == $this->boardsize - 4 && ($x == 3 || $x == ceil($this->boardsize / 2) - 1 || $x == $this->boardsize - 4)))
				{
					$stone = 'ko';
				}
				else if ($this->boardsize < 13 && $this->boardsize > 7 && ($y == 2 && ($x == 2 || $x == ceil($this->boardsize / 2) - 1 || $x == $this->boardsize - 3)))
				{
					$stone = 'ko';
				}
				else if ($this->boardsize < 13 && $this->boardsize > 7 && ($y == ceil($this->boardsize / 2) - 1 && ($x == 2 || $x == ceil($this->boardsize / 2) - 1 || $x == $this->boardsize - 3)))
				{
					$stone = 'ko';
				}
				else if ($this->boardsize < 13 && $this->boardsize > 7 && ($y == $this->boardsize - 3 && ($x == 2 || $x == ceil($this->boardsize / 2) - 1 || $x == $this->boardsize - 3)))
				{
					$stone = 'ko';
				}
				else if ($this->boardsize == 7 && ($y == 2 && ($x == 2 || $x == $this->boardsize - 3)))
				{
					$stone = 'ko';
				}
				else if ($this->boardsize == 7 && ($y == $this->boardsize - 3 && ($x == 2 || $x == $this->boardsize - 3)))
				{
					$stone = 'ko';
				}
				else
				{
					$stone = 'mm';
				}
			}
		}

		$this->board[$y][$x] = $stone;
	}


	// do a move a user requested, take prisoners
	function do_move($y, $x)
	{
		$this->board[$y][$x] = $this->color;

		if ($this->color == 'b')
		{
			$this->dummy = $this->group_runs_out_of_liberties($y, $x, true, 'w');
		}
		else if ($color == 'w')
		{
			$this->dummy = $this->group_runs_out_of_liberties($y, $x, true, 'b');
		}
		else
		{
			$this->error = 'Given color attribute is not known.';
		}
	}


	// return true if the move is ko and therfore not allowed, else return false
	function is_ko($y, $x)
	{
		$backup = $this->board;
		$this->do_move($y, $x);

		for ($i = 0; $i < $this->boardsize; ++$i)
		{
			for ($j = 0; $j < $this->boardsize; ++$j)
			{
				if ($this->board[$i][$j] == $this->oldboard[$i][$j])
				{
					$ko = true;
				}
				else
				{
					$ko = false;
					break;
				}
			}

			if ($ko == false)
			{
				break;
			}
		}

		$this->board = $backup;
		return $ko;
	}


	// return true if the point on the board is a free space
	function is_freedom($y, $x)
	{
		return ((('b' != $this->board[$y][$x]) && ('w' != $this->board[$y][$x])) ? true : false);
	}


	// return true if the move is not a suicide, this function does not check for ko se the is_ko function for this
	function is_legal_move($y, $x)
	{
		if (('b' != $this->board[$y][$x]) && ('w' != $this->board[$y][$x]))
		{
			$backup = $this->board[$y][$x];

			if ($this->color == 'b')
			{
				$this->board[$y][$x] = 'b';

				if ($this->freedom_of_group($y, $x) < 1)
				{
					$this->board[$y][$x] = 'b';

					if ( ! $this->group_runs_out_of_liberties($y, $x, false, 'w'))
					{
						$this->board[$y][$x] = $backup;
						return false;
					}
					else
					{
						$this->board[$y][$x] = $backup;
						return true;
					}
				}

				$this->board[$y][$x] = $backup;
				return true;
			}
			else if ($this->color == 'w')
			{
				$this->board[$y][$x] = 'w';

				if ($this->freedom_of_group($y, $x) < 1)
				{
					if ( ! $this->group_runs_out_of_liberties($y, $x, false, 'b'))
					{
						$this->board[$y][$x] = $backup;
						return false;
					}
					else
					{
						$this->board[$y][$x] = $backup;
						return true;
					}
				}

				$this->board[$y][$x] = $backup;
				return true;
			}
			else
			{
				$this->error = 'Given color attribute is not known.';
				return false;
			}
		}
	}



	function is_move_possible($y, $x)
	{
		return ( ! $this->is_ko($y, $x) && $this->is_legal_move($y, $x));
	}


	// delete the stones last checked by another function
	function delete_current_visited( )
	{
		$pos_to_delete = strtok($this->visited, ',');

		while (strlen($pos_to_delete) >= 1)
		{
			if (strlen($pos_to_delete) >= 1)
			{
				if (intval($pos_to_delete) >= 100)
				{
					$y = floor(intval($pos_to_delete) / 100);
				}
				else
				{
					$y = 0;
				}

				$x = intval($pos_to_delete) - ($y * 100);

				if ($color == 'b')
				{
					$rcolor = 'e';
				}
				else if ($color == 'w')
				{
					$rcolor = 'r';
				}
				else
				{
					echo 'Error';
					die;
				}

				if ( ! strstr($this->removed, $rcolor . chr($y + 97) . chr($x + 97)))
				{
					$this->removed = $this->removed . $rcolor . chr($y + 97) . chr($x + 97);
				}

				$this->board[$y][$x] = $hits->insert_empty($y, $x);
			}

			$pos_to_delete = strtok(',');
		}
	}


	// return all board positions which are connected by lines to the current one
	function is_connected_to($y, $x)
	{
		$connected = array( );

		if ($y == 0)
		{
			if ($x == 0)
			{
				$connected[0] = $y + 1;
				$connected[1] = $x;
				$connected[2] = $y;
				$connected[3] = $x + 1;
			}
			else if ($x == $this->boardsize - 1)
			{
				$connected[0] = $y + 1;
				$connected[1] = $x;
				$connected[2] = $y;
				$connected[3] = $x - 1;
			}
			else
			{
				$connected[0] = $y;
				$connected[1] = $x - 1;
				$connected[2] = $y;
				$connected[3] = $x + 1;
				$connected[4] = $y + 1;
				$connected[5] = $x;
			}
		}
		else if ($y == $this->boardsize - 1)
		{
			if ($x == 0)
			{
				$connected[0] = $y - 1;
				$connected[1] = $x;
				$connected[2] = $y;
				$connected[3] = $x + 1;
			}
			else if ($x == $this->boardsize - 1)
			{
				$connected[0] = $y - 1;
				$connected[1] = $x;
				$connected[2] = $y;
				$connected[3] = $x - 1;
			}
			else
			{
				$connected[0] = $y;
				$connected[1] = $x - 1;
				$connected[2] = $y;
				$connected[3] = $x + 1;
				$connected[4] = $y - 1;
				$connected[5] = $x;
			}
		}
		else
		{
			if ($x == 0)
			{
				$connected[0] = $y - 1;
				$connected[1] = $x;
				$connected[2] = $y + 1;
				$connected[3] = $x;
				$connected[4] = $y;
				$connected[5] = $x + 1;
			}
			else if ($x == $this->boardsize - 1)
			{
				$connected[0] = $y - 1;
				$connected[1] = $x;
				$connected[2] = $y + 1;
				$connected[3] = $x;
				$connected[4] = $y;
				$connected[5] = $x - 1;
			}
			else
			{
				$connected[0] = $y + 1;
				$connected[1] = $x;
				$connected[2] = $y - 1;
				$connected[3] = $x;
				$connected[4] = $y;
				$connected[5] = $x + 1;
				$connected[6] = $y;
				$connected[7] = $x - 1;
			}
		}

		return $connected;
	}


	// return true if group is dead
	function group_runs_out_of_liberties($y, $x, $delete_dead_stones)
	{
		$they_are_dead_jim = false;
		$counter           = 0;
		$to_check          = $this->is_connected_to($y, $x);

		foreach ($to_check as $number => $coord)
		{
			if ($counter % 2 == 0)
			{
				if ($this->board[$to_check[$counter]][$to_check[$counter + 1]] == $this->color)
				{
					if ($this->freedom_of_group($to_check[$counter], $to_check[$counter + 1]) < 1)
					{
						if ($delete_dead_stones)
						{
							$this->delete_current_visited($this->color);
						}

						$they_are_dead_jim = true;
					}
				}
			}

			++$counter;
		}

		return $they_are_dead_jim;
	}


	// returns the owner and the number of freedoms grouped together
	function group_of_freedoms($y, $x)
	{
		$this->visited = ',';
		$this->stones_visited = ',';

		$to_visit_string = ',' . strval(($y * 100) + $x) . ',';
		$this->owner           = '';
		$this->freedom         = 0;

		while (strlen($to_visit_string) > 2)
		{
			$cord_to_visit = strtok(substr($to_visit_string, 1), ',');

			if (intval($cord_to_visit) >= 100)
			{
				$y = floor(intval($cord_to_visit) / 100);
			}
			else
			{
				$y = 0;
			}

			$x = intval($cord_to_visit) - ($y * 100);
			$to_visit_string = str_replace(',' . $cord_to_visit . ',', ',', $to_visit_string);
			$counter = 0;
			$to_check = is_connected_to($y, $x);

			foreach ($to_check as $number => $coord)
			{
				if ($counter % 2 == 0)
				{
					if ( ! strstr($this->stones_visited, ',' . strval((($to_check[$counter]) * 100) + $to_check[$counter + 1]) . ','))
					{
						if ( ! $this->is_freedom($to_check[$counter], $to_check[$counter + 1]))
						{
							if ('' != $this->owner)
							{
								if ($this->owner != $this->board[$to_check[$counter]][$to_check[$counter + 1]])
								{
									$this->owner = 'f';
								}
							}
							else
							{
								$this->owner = $this->board[$to_check[$counter]][$to_check[$counter + 1]];
							}

							$this->stones_visited .= strval((($to_check[$counter]) * 100) + $to_check[$counter + 1]) . ',';
						}
					}

					if ( ! strstr($this->visited, ',' . strval((($to_check[$counter]) * 100) + $to_check[$counter + 1]) . ','))
					{
						if ($this->is_freedom($to_check[$counter], $to_check[$counter + 1]))
						{
							++$this->freedom;
							$to_visit_string .= strval((($to_check[$counter]) * 100) + $to_check[$counter + 1]) . ',';
						}
					}
				}

				++$counter;
			}

			$this->visited .= strval(($y * 100) + $x) . ',';
		}

#		return array(
#			$owner ,
#			$freedom ,
#		);
	}


	// how many liberties does a group of stones have
	function freedom_of_group($y, $x)
	{
		$this->visited = ',';
		$this->freedoms_visited = ',';

		$to_visit_string = ',' . strval(($y * 100) + $x) . ',';
		$this->freedom         = 0;

		while (strlen($to_visit_string) > 2)
		{
			$cord_to_visit = strtok(substr($to_visit_string, 1), ',');

			if (intval($cord_to_visit) >= 100)
			{
				$y = floor(intval($cord_to_visit) / 100);
			}
			else
			{
				$y = 0;
			}

			$x = intval($cord_to_visit) - ($y * 100);
			$to_visit_string = str_replace(',' . $cord_to_visit . ',', ',', $to_visit_string);
			$counter = 0;
			$to_check = $this->is_connected_to($y, $x);

			foreach ($to_check as $number => $coord)
			{
				if ($counter % 2 == 0)
				{
					if ( ! strstr($this->freedoms_visited, ',' . strval((($to_check[$counter]) * 100) + $to_check[$counter + 1]) . ','))
					{
						if ($this->is_freedom($to_check[$counter], $to_check[$counter + 1]))
						{
							++$this->freedom;
							$this->freedoms_visited .= strval((($to_check[$counter]) * 100) + $to_check[$counter + 1]) . ',';
						}
					}

					if ( ! strstr($this->visited, ',' . strval((($to_check[$counter]) * 100) + $to_check[$counter + 1]) . ','))
					{
						if ($this->board[$to_check[$counter]][$to_check[$counter + 1]] == $this->color)
						{
							$to_visit_string .= strval((($to_check[$counter]) * 100) + $to_check[$counter + 1]) . ',';
						}
					}
				}

				++$counter;
			}

			$this->visited .= strval(($y * 100) + $x) . ',';
		}

		return $this->freedom;
	}


	// creates an empty board to start with
	function create_empty_board( )
	{
		$this->board = array( );

		for ($y = 0; $y < $this->boardsize; ++$y)
		{
			for ($x = 0; $x < $this->boardsize; ++$x)
			{
				$this->board[$y][$x] = $this->insert_empty($y, $x);
			}
		}
	}


	function get_empty_board( )
	{
		// save the current board
		$temp_board = $this->get_board( );

		// create a new empty board
		$this->create_empty_board( );

		// save the new empty board
		$return_board = $this->board;

		// replace the original board
		$this->set_board($temp_board);

		// return the empty board to the script
		return $return_board;
	}


	function get_moved_stone_x( )
	{
		return $this->moved_stone_x;
	}


	function get_moved_stone_y( )
	{
		return $this->moved_stone_y;
	}



} // end of iShudan class


// other non-class but required functions

// translate numeric coordinates to alphanumeric coordinates
function xtoalt($xcord)
{
	if ($xcord < 8)
	{
		return chr(65 + $xcord);
	}
	else
	{
		return chr(66 + $xcord);
	}
}


// translates alphanumeric coordinates to numeric coordinates
function stringtocord($costring)
{
	$color = substr($costring, 0, 1);
	$y     = ord(substr($costring, 1, 1)) - 97;
	$x     = ord(substr($costring, 2, 1)) - 97;
	return array(
		$color ,
		$y ,
		$x ,
	);
}




?>